package dkbta.util;

import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import dkbta.Config;

/**
 * A global logger for the system. A wrapper over <tt>java.util.logging.Logger</tt> in
 * the sense that it can be used even when not initialized without throwing exceptions
 * (only the log messages will be suppressed)
 * 
 * @author Uri Kanonov
 */
public class Log{
	/** The wrapper's single instance */
	private static Log _log = new Log();

	/**
	 * The actual logger's single instance, it is initialized in
	 * <tt>init(String unitId)</tt>
	 */
	private static Logger _logger;

	/** To keep others from instantiating this class */
	private Log(){
	}

	public boolean isLoggable(Level level){
		return _logger.isLoggable(level);
	}

	public void fine(String msg){
		if (_logger != null){
			_logger.fine(msg);
		}
	}

	public void fine(String msg, Throwable e){
		if (_logger != null){
			_logger.log(Level.FINE, msg, e);
		}
	}

	public void finer(String msg){
		if (_logger != null){
			_logger.finer(msg);
		}
	}

	public void finer(String msg, Throwable e){
		if (_logger != null){
			_logger.log(Level.FINER, msg, e);
		}
	}

	public void finest(String msg){
		if (_logger != null){
			_logger.finest(msg);
		}
	}

	public void info(String msg){
		if (_logger != null){
			_logger.info(msg);
		}
	}

	@SuppressWarnings("unused")
	private void log(Level level, String msg, Throwable e){
		if (_logger != null){
			_logger.log(level, msg, e);
		}
	}

	@SuppressWarnings("unused")
	private void log(Level level, String msg){
		if (_logger != null){
			_logger.log(level, msg);
		}
	}

	public void warning(Throwable e){
		if (_logger != null){
			_logger.log(Level.WARNING, "", e);
		}
	}

	public void warning(String msg, Throwable e){
		if (_logger != null){
			_logger.log(Level.WARNING, msg, e);
		}
	}

	public void severe(String msg){
		if (_logger != null){
			_logger.severe(msg);
		}
	}

	public void severe(String msg, Throwable e){
		if (_logger != null){
			_logger.log(Level.SEVERE, msg, e);
		}
	}

	public void warning(String msg){
		if (_logger != null){
			_logger.warning(msg);
		}
	}

	/**
	 * Initializes the logger with a given unit-id. The log file will be
	 * <b>dkbta-unitId.log</b>
	 * 
	 * @param unitId The id of the unit for which this log is created
	 * @return The single logger instance
	 * @throws IOException If the logger can't be initialized
	 */
	public static Log init(String unitId) throws IOException{
		_logger = initLogger("./files/logs/dkbta-" + unitId + ".log");
		return _log;
	}

	/**
	 * Obtains the single logger instance
	 * 
	 * @return The single logger instance
	 */
	public static Log getLogger(){
		return _log;
	}

	/**
	 * Initializes a logger that writes to the given file
	 * 
	 * @param logFile Log file name
	 * @return The initialized logger
	 * @throws IOException If a logger couldn't be initialized
	 */
	private static Logger initLogger(String logFile) throws IOException{
		// Initializing the logger
		Handler handler = null;
		try{
			// Creating a file handler
			handler = new FileHandler(logFile);
		}catch(IOException e){
			throw new IOException("Couldn't initialize the logger: " + e.getMessage());
		}
		// Adding it to the logger
		Logger logger = Logger.getLogger(logFile);
		logger.addHandler(handler);
		handler.setFormatter(new Formatter(){
			private final DateFormat _df = new SimpleDateFormat("HH:mm:ss.SSS dd.MM.yyyy");

			@Override
			public String format(LogRecord rec){
				StringBuilder sb = new StringBuilder();
				// Formatting the message
				sb.append("[").append(_df.format(rec.getMillis())).append("] ").append(
					rec.getMessage()).append("\r\n");

				// Formatting the throwable if such exists
				Throwable t = rec.getThrown();
				if (t != null){
					t.printStackTrace(new PrintWriter(new StringBuilderWriter(sb), true));
					sb.append("\r\n");
				}

				return sb.toString();
			}
		});
		logger.setLevel(Level.parse(Config.getString("MINIMUM_LOGGING_LEVEL", "FINER")));

		return logger;
	}
}
